﻿#pragma once

#include "pch.h"
#include "framework.h"

#pragma pack(push)
#pragma pack(1)
class CPacket
{
public:
	// 默认初始化（用于接收解析）
	CPacket() :sHead(0), nLength(0), sCmd(0), sSum(0) {}
	// 构造发送包（数据序列化）
	CPacket(WORD nCmd, const BYTE* pData, size_t nSize)
	{
		sHead = 0xFEFF;
		nLength = nSize + 4;// 数据长度 + Cmd(2字节) + Sum(2字节)
		sCmd = nCmd;
		if (nSize > 0)
		{
			strData.resize(nSize);
			memcpy((void*)strData.c_str(), pData, nSize);
		}
		else
		{
			strData.clear();
		}
		sSum = 0;
		for (size_t j = 0; j < strData.size(); j++)
		{
			sSum += BYTE(strData[j]) & 0xFF;//求和
		}
	}
	CPacket(const CPacket& pack)
	{
		sHead = pack.sHead;
		nLength = pack.nLength;
		sCmd = pack.sCmd;
		strData = pack.strData;
		sSum = pack.sSum;
	}

	// 从字节流解析数据包（用于接收端）
	CPacket(const BYTE* pData, size_t& nSize)
	{
		size_t i = 0;
		// 查找包头 0xFEFF
		for (; i < nSize; i++)
		{
			if (*(WORD*)(pData + i) == 0xFEFF)
			{
				sHead = *(WORD*)(pData + i);
				i += 2;//防止特殊情况，包头长度 //跳过包头
				break;
			}
		}
		//解析失败 //检查剩余数据是否足够解析
		if (i + 4 + 2 + 2 > nSize)//包数据可能不全，或者报头未能全部接收到
		{
			nSize = 0;// 数据不完整，标记解析失败
			return;
		}
		//解析到了数据 
		//解析长度、命令、数据
		nLength = *(DWORD*)(pData + i); i += 4;
		if (nLength + i > nSize)
		{
			//包没有完全接收到
			nSize = 0;
			return;
		}
		sCmd = *(WORD*)(pData + i); i += 2;
		if (nLength > 4)
		{
			strData.resize(nLength - 2 - 2);// 扣除Cmd和Sum长度
			memcpy((void*)strData.c_str(), pData + i, nLength - 4);
			i += nLength - 4;
		}
		sSum = *(WORD*)(pData + i); i += 2;
		// 校验和验证
		WORD sum = 0;
		for (size_t j = 0; j < strData.size(); j++)
		{
			sum += BYTE(strData[j]) & 0xFF;
		}
		if (sum == sSum)
		{
			//解析成功
			nSize = i;// +nLength + 2 + 4;// head2 + nLength4 data...//成功解析，返回已处理字节数
			return;
		}
		nSize = 0;//失败，nSize是输入输出型参数
	}
	~CPacket(){}
	CPacket& operator=(const CPacket& pack)
	{
		if (this != &pack)
		{
			sHead = pack.sHead;
			nLength = pack.nLength;
			sCmd = pack.sCmd;
			strData = pack.strData;
			sSum = pack.sSum;
		}
		return *this;
	}
	int Size()
	{
		return nLength + 2 + 4;						//获取包数据大小
	}
	// 序列化方法（生成完整数据包）
	const char* Data()
	{
		strOut.resize(nLength + 6);					// 总长度 = 包头(2) + 长度字段(4) + 包体(nLength)
		BYTE* pData = (BYTE*)strOut.c_str();		//pData是一个指针
		*(WORD*)pData = sHead; pData += 2;			// 写入包头
		*(DWORD*)(pData) = nLength; pData += 4;		// 写入长度
		*(WORD*)pData = sCmd; pData += 2;			// 写入命令
		memcpy(pData, strData.c_str(), strData.size()); pData += strData.size();// 写入数据
		*(WORD*)pData = sSum;						// 写入校验和
		return strOut.c_str();
		/*
		1.通过指针操作直接操作内存，确保网络字节序一致性
		2.strOut 缓存序列化结果，避免多次内存分配影响性能
		*/
	}

public:
	WORD sHead;			//固定位 0XFEFF  固定包头标识（0xFEFF）
	DWORD nLength;		//包体长度（数据部分长度 + 4）(strData.size() + 4)动态
	WORD sCmd;			//控制命令，如控制请求
	std::string strData;//包数据，实际数据内容
	WORD sSum;			//和校验，用于验证数据完整性
	std::string strOut;	//整个包的数据，序列化后的完整数据包缓存
};
#pragma pack(pop)
/*CPacket总结
1：​​协议设计​​：
​​固定包头​​（0xFEFF）用于快速识别数据包起始。
​​长度字段​​（nLength）支持动态数据长度，避免固定长度浪费带宽。
​​校验和​​（sSum）保障数据完整性。
​​2：性能优化​​：
​​紧凑内存布局​​（#pragma pack(1)）减少传输数据量。
​​零拷贝序列化​​：通过指针直接操作内存，避免中间拷贝。
3：​​扩展性​​：
​​命令字段​​（sCmd）支持多业务场景（如文件传输、控制指令）。
​​std::string 管理数据​​：动态内存分配简化数据操作。*/

typedef struct MouseEvent
{
	MouseEvent()
	{
		nAction = 0;
		nButton = -1;//左中右021，所以要置为-1
		ptXY.x = 0;
		ptXY.y = 0;
	}
	WORD nAction;//点击，移动，双击
	WORD nButton;//左键，右键，中键
	POINT ptXY;//坐标 //POINT是xy的结构体类型，包含xy

}MOUSEEV, *PMOUSEEV;

class CServerSocket
{
public:
	static CServerSocket* getInstance()
	{
		//静态函数没有this指针，无法访问成员变量
		if (m_instance == NULL)
			m_instance = new CServerSocket();
		return m_instance;
	}
	bool InitSocket()
	{
		if (m_sock == -1) return false;
		sockaddr_in serv_adr;
		memset(&serv_adr, 0, sizeof(serv_adr));
		serv_adr.sin_family = AF_INET;
		serv_adr.sin_addr.s_addr = INADDR_ANY;
		serv_adr.sin_port = htons(9527);

		if (bind(m_sock, (sockaddr*)&serv_adr, sizeof(serv_adr)) == -1)
			return false;
		if (listen(m_sock, 1) == -1)
			return false;
		
		return true;
	}
	bool AcceptClient()
	{
		sockaddr_in client_adr;
		int cli_sz = sizeof(client_adr);
		SOCKET m_client = accept(m_sock, (sockaddr*)&client_adr, &cli_sz);
		if (m_client == -1) return false;
		return true;
	}
#define BUFFER_SiZE 4096
	//接收命令
	int DealCommand()
	{
		if (m_client == -1) return -1;
		//char buffer[1024] = "";
		char* buffer = new char[BUFFER_SiZE];
		memset(buffer, 0, BUFFER_SiZE);
		size_t index = 0;
		while (true)
		{
			size_t len = recv(m_client, buffer + index, BUFFER_SiZE - index, 0);
			if (len <= 0) return -1;

			index += len;
			len = index;
			m_packet = CPacket((BYTE*)buffer, len);//用这个类去解析读取的包
			if (len > 0)
			{
				memmove(buffer, buffer + len, BUFFER_SiZE - len);
				index -= len;
				return m_packet.sCmd;
			}
		}
		return -1;
	}
	bool Send(const char* pData, int nSize)
	{
		if (m_client == -1) 
			return false;
		return send(m_client, pData, nSize, 0) > 0;
	}
	bool Send(CPacket& pack)
	{
		if (m_client == -1)
			return false;
		return send(m_client, pack.Data(), pack.Size(), 0) > 0;
	}
	//获取文件列表
	bool GetFilePath(std::string& strPath)
	{
		if ((m_packet.sCmd >= 2) && (m_packet.sCmd <= 4))
		{
			strPath = m_packet.strData;
			return true;
		}
		return false;
	}
	bool GetMouseEvent(MOUSEEV& mouse)
	{
		if (m_packet.sCmd == 5)
		{
			memcpy(&mouse, m_packet.strData.c_str(), sizeof(MOUSEEV));
			return true;
		}
		return false;
	}
private:
	SOCKET m_client;
	SOCKET m_sock;
	CPacket m_packet;
	CServerSocket& operator=(const CServerSocket& ss) {}
	CServerSocket(const CServerSocket& ss)	
	{
		m_sock = ss.m_sock;
		m_client = ss.m_client;
	}
	CServerSocket()
	{
		//m_sock = INVALID_SOCKET;//下面有创建
		m_client = INVALID_SOCKET;//-1
		if (InitSockEnv() == FALSE)
		{
			MessageBox(NULL, _T("无法初始化套接字环境,请检查网络设置!"), _T("初始化错误!"), MB_OK | MB_ICONERROR);
			exit(0);
		}
		m_sock = socket(PF_INET, SOCK_STREAM, 0);
	}
	~CServerSocket()
	{
		closesocket(m_sock);
		WSACleanup();
	}
	BOOL InitSockEnv()
	{
		WSADATA data;
		if (WSAStartup(MAKEWORD(1, 1), &data) != 0)
			return FALSE;
		return TRUE;
	}
	static void releaseInstance()
	{
		if (m_instance != NULL)
		{
			CServerSocket* tmp = m_instance;
			m_instance = NULL;
			delete tmp;
		}
	}
	static CServerSocket* m_instance;//静态实例，怎么去清理？在堆上，是全局得，需要我们手动申请和手动释放.
	//但是它又是static得，根据RAII思想，设计一个CHelper类，然后让这个CHelper来释放m_instance

	//RAII，
	class CHelper
	{
	public:
		CHelper()
		{
			CServerSocket::getInstance();
		}
		~CHelper()
		{
			CServerSocket::releaseInstance();
		}
	};
	static CHelper m_helper;//根据进程生命周期来的，声明
};

//extern CServerSocket server;

/*CServerSocket总结
饿汉模式
理解垃圾回收类
理解static，为什么是声明定义分开？以及设计模式
*/

